﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncLab
{
    class Program
    {
        static void Main(string[] args)
        {
            //TaskWithEx();
            CachedDownloads();
            Console.ReadLine();
        }

        //private static void AwaiterTest()
        //{
        //    Console.WriteLine("threadid:" + Thread.CurrentThread.ManagedThreadId);

        //    Task<string> t = DoSomeAsync();
        //    t.GetAwaiter<string>();
        //    //var awaitableTask = t.ConfigureAwait(true);
        //    //await awaitableTask;
        //    var awaiter = t.GetAwaiter<string>(); //awaitableTask.GetAwaiter();            

        //    string result = awaiter.GetResult();
        //    Console.WriteLine("threadid:" + Thread.CurrentThread.ManagedThreadId);
        //    Console.ReadLine();
        //}

        //static async Task<string> DoSomeAsync()
        //{
        //    Task<string> t = new Task<string>(() => { return "done"; });
        //    var awaitableT = t.ConfigureAwait<string>(true);
        //    await awaitableT;
        //    WebClient client = new WebClient();
        //    await client.OpenReadTaskAsync("http://bing.com");
        //    Console.WriteLine("threadid:" + Thread.CurrentThread.ManagedThreadId);
        //    return "done";
        //}

        static void TaskWithEx()
        {
            Task[] taskArray = new Task[6];
            for (int i = 0; i < 6; i++)
            {
                taskArray[i] = Task.Factory.StartNew((index) =>
                {
                    throw new Exception("task Ex" + index);
                }, i);
                //.ContinueWith((t,index) =>
                //{
                //    if (t.Exception!=null)
                //    {
                //        //throw new Exception("continue Ex"+index, t.Exception);
                //        throw t.Exception.Flatten();
                //    }
                //},i);
            }
            try
            {
                try
                {
                    Task.WhenAll(taskArray).Wait();
                }
                catch (AggregateException ex)
                {
                    throw ex.Flatten();
                    //AggregateException flattendEx = ex;//.Flatten();
                    //foreach (var innerEx in flattendEx.InnerExceptions)
                    //{
                    //    Console.WriteLine(innerEx);
                    //}

                    //use AggregateException.Handle to handle inner exceptions
                    //ex.Handle((innerEx) =>
                    //{
                    //    Console.WriteLine(innerEx);
                    //    //unhandled exception would be thrown if returns false here
                    //    return true;
                    //});
                }
            }
            catch (Exception flattenEx)
            {
                Console.WriteLine(flattenEx);
            }
        }

        // Holds the results of download operations. 
        static ConcurrentDictionary<string, string> cachedDownloads =
           new ConcurrentDictionary<string, string>();

        // Asynchronously downloads the requested resource as a string. 
        public static Task<string> DownloadString(string address)
        {
            // First try to retrieve the content from cache. 
            string content;
            if (cachedDownloads.TryGetValue(address, out content))
            {
                return Task.FromResult<string>(content);
            }

            // If the result was not in the cache, download the  
            // string and add it to the cache. 
            return Task.Run(async () =>
            {
                content = await new WebClient().DownloadStringTaskAsync(address);
                cachedDownloads.TryAdd(address, content);
                return content;
            });
        }

        // Asynchronously downloads the requested resource as a string. 
        public static async Task<string> DownloadStringAsync(string address)
        {
            // First try to retrieve the content from cache. 
            string content;
            if (cachedDownloads.TryGetValue(address, out content))
            {
                return content;
            }

            // If the result was not in the cache, download the  
            // string and add it to the cache. 

            content = await new WebClient().DownloadStringTaskAsync(address);
            cachedDownloads.TryAdd(address, content);
            return content;

        }

        static void CachedDownloads()
        {
            // The URLs to download. 
            string[] urls = new string[]
      {
         "http://msdn.microsoft.com",
         "http://www.bing.com",
         "http://www.microsoft.com"
      };

            // Used to time download operations.
            Stopwatch stopwatch = new Stopwatch();

            // Compute the time required to download the URLs.
            stopwatch.Start();
            var downloads = from url in urls
                            select DownloadStringAsync(url);
            Task.WhenAll(downloads).ContinueWith(results =>
            {
                stopwatch.Stop();

                // Print the number of characters download and the elapsed time.
                Console.WriteLine("Retrieved {0} characters. Elapsed time was {1} ms.",
                   results.Result.Sum(result => result.Length),
                   stopwatch.ElapsedMilliseconds);
            })
            .Wait();

            // Perform the same operation a second time. The time required 
            // should be shorter because the results are held in the cache.
            stopwatch.Restart();
            downloads = from url in urls
                        select DownloadStringAsync(url);
            Task.WhenAll(downloads).ContinueWith(results =>
            {
                stopwatch.Stop();

                // Print the number of characters download and the elapsed time.
                Console.WriteLine("Retrieved {0} characters. Elapsed time was {1} ms.",
                   results.Result.Sum(result => result.Length),
                   stopwatch.ElapsedMilliseconds);
            })
            .Wait();
        }
    }
}
